package com.autonavi.yunda.yunji.controller;

import com.autonavi.yunda.yunji.common.exception.AmapException;
import com.autonavi.yunda.yunji.common.exception.AmapExceptionCode;
import com.autonavi.yunda.yunji.common.exception.AmapPreconditions;
import com.autonavi.yunda.yunji.controller.interceptor.NeedOperationLog;
import com.autonavi.yunda.yunji.controller.request.FunctionConfigRequest;
import com.autonavi.yunda.yunji.core.enums.OperationRecordType;
import com.autonavi.yunda.yunji.core.model.condition.FunctionQueryCondition;
import com.autonavi.yunda.yunji.core.model.dto.FunctionConfigDTO;
import com.autonavi.yunda.yunji.core.model.dto.FunctionProjectMappingDTO;
import com.autonavi.yunda.yunji.core.service.FunctionConfigService;
import com.autonavi.yunda.yunji.core.service.FunctionProjectMappingService;
import com.autonavi.yunda.yunji.core.vo.response.FunctionGroupVO;
import com.autonavi.yunda.yunji.core.vo.response.FunctionPageVO;
import com.autonavi.yunda.yunji.core.vo.response.SimpleFunctionConfigVO;
import com.autonavi.yunda.yunji.service.BucUserService;
import com.autonavi.yunda.yunji.service.OperationRecordService;
import com.autonavi.yunda.yunji.service.ProjectAuthService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author cizhong.wcs
 * @date 2022/3/7 7:33 下午
 */
@Slf4j
@RestController
@RequestMapping("/function")
public class FunctionController {
    @Autowired
    private FunctionConfigService functionConfigService;
    @Resource
    private OperationRecordService operationRecordService;
    @Resource
    private FunctionProjectMappingService functionProjectMappingService;
    @Resource
    private ProjectAuthService projectAuthService;
    @Resource
    private BucUserService bucUserService;


    private void checkFunctionPermission(Long functionId) {
        List<String> authorizedProjectIds = projectAuthService.listAuthorizedProjectIds();
        List<String> functionProjectIds = functionProjectMappingService.listByFunctionId(functionId).stream().map(FunctionProjectMappingDTO::getProjectId).collect(Collectors.toList());
        functionProjectIds.retainAll(authorizedProjectIds);
        if (functionProjectIds.isEmpty()) {
            throw new AmapException(AmapExceptionCode.NO_PERMISSION_ERROR, "没有该函数权限");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @NeedOperationLog(operationName = "function.add")
    @PostMapping("add")
    public Long add(@RequestBody FunctionConfigRequest request) {
        //1. 检查数据库中的该用户拥有权限的项目id是否包含本次操作的项目id（如果不包含，抛出异常）
        this.projectAuthService.checkPermissionAny(request.getProjectIds());
        //2. 请求信息中是否含有雇员id
        String employeeId = bucUserService.getCurEmployeeIdOrNull();
        //3. 插入到yunji_function_config
        FunctionConfigDTO functionConfigDTO = request.toAddDTO(bucUserService.getCurEmployeeIdOrNull());
        functionConfigService.add(functionConfigDTO);
        Long id = functionConfigDTO.getId();
        //4. 更新映射关系
        functionProjectMappingService.updateMapping(id, request.getProjectIds(), employeeId);
        //5. 添加操作日志
        operationRecordService.add(OperationRecordType.FunctionSubType.CREATE, id.toString());
        return id;
    }

    @Transactional(rollbackFor = Exception.class)
    @NeedOperationLog(operationName = "function.update")
    @PostMapping("update")
    public String update(@RequestBody FunctionConfigRequest request) {
        AmapPreconditions.checkArgumentForUser(request.getId() != null, "id 不能为空");
        checkFunctionPermission(request.getId());
        String employeeId = bucUserService.getCurEmployeeIdOrNull();
        functionConfigService.update(request.toUpdateDTO(employeeId));
        functionProjectMappingService.updateMapping(request.getId(), request.getProjectIds(), employeeId);
        operationRecordService.add(OperationRecordType.FunctionSubType.UPDATE, request.getId().toString());
        return "success";
    }

    @PostMapping("list")
    public FunctionPageVO list(@Validated @RequestBody FunctionQueryCondition request) {
        AmapPreconditions.checkNotNull(request.getPageNo(), "pageNo不能为空");
        AmapPreconditions.checkNotNull(request.getPageSize(), "pageSize不能为空");
        List<String> projectIds = projectAuthService.listAuthorizedProjectIds();
        Map<Long, Set<String>> functionProjectIdMap = functionProjectMappingService.fetchFunctionProjectIdMap(projectIds);
        request.getIds().addAll(functionProjectIdMap.keySet());
        FunctionPageVO functionPageVO = functionConfigService.listPage(request);
        functionPageVO.getFunctionConfigDTOList().forEach(functionVO -> functionVO.setProjectIds(functionProjectIdMap.get(functionVO.getId())));
        return functionPageVO;
    }

    /**
     * 分页查询以functionName分组的接口配置
     */
    @PostMapping("listFunctionGroup")
    public FunctionGroupVO listFunctionGroup(@Validated @RequestBody FunctionQueryCondition condition) {
        List<String> projectIds = projectAuthService.listAuthorizedProjectIds();
        Map<Long, Set<String>> functionProjectIdMap = functionProjectMappingService.fetchFunctionProjectIdMap(projectIds);
        condition.getIds().addAll(functionProjectIdMap.keySet());
        FunctionGroupVO functionGroupVO = functionConfigService.listFunctionGroup(condition);
        functionGroupVO.getFunctionList().forEach(functionVOList ->
                functionVOList.forEach(funVO -> funVO.setProjectIds(functionProjectIdMap.get(funVO.getId())))
        );
        return functionGroupVO;
    }

    @PostMapping("listWithoutPage")
    public List<SimpleFunctionConfigVO> listWithoutPage(@Validated @RequestBody(required = false) FunctionQueryCondition condition) {
        if (StringUtils.isNotBlank(condition.getProjectId())) {
            List<Long> functionIds = functionProjectMappingService.listFunctionIdsByProjectId(condition.getProjectId());
            if (functionIds.isEmpty()) {
                return new ArrayList<>();
            }
            condition.setIds(new HashSet<>(functionIds));
        }
        return functionConfigService.listSimpleFunctionVO(condition);
    }

    @NeedOperationLog(operationName = "function.publish")
    @GetMapping("publish")
    public void publish(@RequestParam Long id) {
        checkFunctionPermission(id);
        functionConfigService.publish(id);
        functionConfigService.notifyEngine(id);
        operationRecordService.add(OperationRecordType.FunctionSubType.PUBLISH, id.toString());
    }

    /**
     * 下线
     */
    @NeedOperationLog(operationName = "function.offline")
    @GetMapping("offline")
    public void offline(@RequestParam Long id) {
        checkFunctionPermission(id);
        functionConfigService.offline(id);
        functionConfigService.notifyEngine(id);
        operationRecordService.add(OperationRecordType.FunctionSubType.OFFLINE, id.toString());
    }

    @NeedOperationLog(operationName = "function.delete")
    @GetMapping("delete")
    public void delete(@RequestParam Long id) {
        checkFunctionPermission(id);
        functionConfigService.delete(id);
        functionConfigService.notifyEngine(id);
        operationRecordService.add(OperationRecordType.FunctionSubType.DELETE, id.toString());
    }

}
